package com.xiaoyu.lanling.widget.audio;

import android.Manifest;
import android.app.Service;
import android.content.Context;
import android.content.pm.PackageManager;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Vibrator;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.OvershootInterpolator;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.app.ActivityCompat;

import com.github.florent37.viewanimator.ViewAnimator;
import com.tbruyelle.rxpermissions2.RxPermissions;
import com.xiaoyu.base.app.AppContext;
import com.xiaoyu.base.app.GlobalUI;
import com.xiaoyu.base.utils.RxUtils;
import com.xiaoyu.lanling.R;
import com.xiaoyu.lanling.activity.base.BaseFragment;
import com.xiaoyu.lanling.event.audio.AudioResultEvent;
import com.xiaoyu.lanling.util.FileCreateUtils;
import com.xiaoyu.lanling.util.MP3PlayerUtils;
import com.xiaoyu.lanling.util.MP3RecorderUtils;

import org.jetbrains.annotations.NotNull;

import java.io.File;
import java.io.IOException;
import java.util.concurrent.TimeUnit;

import in.srain.cube.concurrent.AppThreads;
import io.reactivex.Flowable;
import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;

import static com.xiaoyu.lanling.widget.audio.AudioRecordViewStatus.NORMAL;
import static com.xiaoyu.lanling.widget.audio.AudioRecordViewStatus.PLAYING;
import static com.xiaoyu.lanling.widget.audio.AudioRecordViewStatus.READY;
import static com.xiaoyu.lanling.widget.audio.AudioRecordViewStatus.RECORDING;

public class AudioRecordViewFragment extends BaseFragment implements AudioRecordButton.AudioRecordGeastureListener, MP3RecorderUtils.AudioRecordStatusListener {


    public static AudioRecordViewFragment newInstance() {
        Bundle args = new Bundle();

        AudioRecordViewFragment fragment = new AudioRecordViewFragment();
        fragment.setArguments(args);
        return fragment;
    }

    private static final int MAX_PROGRESS = 1000;
    private static final int MAX_DURATION_IN_SECOND = 90;

    private View mView;
    private TextView mTitleNormal;
    private TextView mTitleRecording;
    private TextView mTitleTime;
    private View mRecordButtonOutsideCircle;
    private AudioRecordButton mRecordButton;
    private ProgressBar mUpdateProgress;
    private ImageView mCenterStatus;
    private ImageButton mDelete;
    private ImageButton mCommit;

    private Object requestTag;
    @AudioRecordViewStatus.AudioRecordViewStatusDef
    private String mStatus = NORMAL;
    private boolean mIsShow = false;
    private long mDurationInMS = 0;
    private long mPlayDurationInMS = 0;
    private AudioResultEvent mAudioResultEvent;
    private File mFile;
    private Disposable mTimeDisposable;

    private MP3RecorderUtils mMP3Recorder;
    private ViewAnimator mInsideCircleBreathing;
    private RxPermissions mRxPermissions;
    private AudioRecordLongClickChangedListener mAudioRecordLongClickChangedListener;
    private Vibrator mVibrator;

    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        mView = inflater.inflate(R.layout.fragment_audio_record_view, container, false);
        return mView;
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        initView();
        initBind();
        initVibrator();
        changeStatus(mStatus);
    }

    @Override
    public void onStop() {
        if (!TextUtils.equals(mStatus, NORMAL)) {
            stopRecord(false, false);
        }
        super.onStop();
    }

    public void lifecycleChange(boolean isShow) {
        if (mIsShow == isShow) {
            return;
        }
        mIsShow = isShow;
        if (!isShow) {
            if (TextUtils.equals(mStatus, RECORDING)) {
                stopRecord(true, false);
            }
        }
    }

    private void initView() {
        mTitleNormal = mView.findViewById(R.id.audio_record_title_normal);
        mTitleRecording = mView.findViewById(R.id.audio_record_title_recording);
        mTitleTime = mView.findViewById(R.id.audio_record_time);
        mRecordButtonOutsideCircle = mView.findViewById(R.id.audio_record_video_outside_circle);
        mRecordButton = mView.findViewById(R.id.audio_record_button);
        mUpdateProgress = mView.findViewById(R.id.audio_record_update_progress);
        mCenterStatus = mView.findViewById(R.id.audio_record_center_status);
        mDelete = mView.findViewById(R.id.audio_record_delete);
        mCommit = mView.findViewById(R.id.audio_record_commit);
    }

    private void initBind() {
        mRecordButton.setRecordGestureListener(this);
        mRecordButton.setOnLongClickListener(v -> {
            if (!TextUtils.equals(mStatus, NORMAL)) {
                return false;
            }
            return mRecordButton.performLongClick(true);
        });
        mRecordButton.setOnClickListener(v -> {
            if (mRecordButton.hasPerformedLongClick()) {
                return;
            }
            animCenterInsideCircle();
            switch (mStatus) {
                case NORMAL:
                    tryStartRecord();
                    break;
                case RECORDING:
                    stopRecord(true, false);
                    break;
                case READY:
                    if (mAudioResultEvent != null) {
                        boolean isPlaying = MP3PlayerUtils.getInstance().start(mAudioResultEvent.getFilePath(), mp -> changeStatus(READY));
                        if (isPlaying) {
                            changeStatus(PLAYING);
                        }
                    }
                    break;
                case PLAYING:
                    stopPlay();
                    break;
            }
        });
        mDelete.setOnClickListener(v -> delete());
        mCommit.setOnClickListener(v -> commit());
    }

    private void initVibrator() {
        Context context = getContext();
        if (context != null) {
            mVibrator = (Vibrator) context.getSystemService(Service.VIBRATOR_SERVICE);
        }
    }

    private void delete() {
        changeStatus(NORMAL);
        mAudioResultEvent = null;
    }

    private void commit() {
        changeStatus(NORMAL);
        if (mAudioResultEvent != null) {
            mAudioResultEvent.post();
        }
        mAudioResultEvent = null;
    }

    private void changeStatus(@AudioRecordViewStatus.AudioRecordViewStatusDef String status) {
        mStatus = status;
        switch (status) {
            case NORMAL:
                normalStatus();
                MP3PlayerUtils.getInstance().release();
                break;
            case RECORDING:
                recordingStatus();
                break;
            case READY:
                readyStatus();
                break;
            case PLAYING:
                playingStatus();
                break;
        }
    }

    private void normalStatus() {
        if (mTitleNormal == null) {
            return;
        }
        mTitleNormal.setText(R.string.audio_record_tip);
        animAlphaView(mTitleNormal, true);
        animAlphaView(mTitleRecording, false);
        animAlphaView(mTitleTime, false);
        animAlphaView(mRecordButtonOutsideCircle, false);
        animAlphaView(mUpdateProgress, false);
        animScaleView(mDelete, false);
        animScaleView(mCommit, false);
        animChangeCenterStatus();
        recordReset();
    }

    private void recordingStatus() {
        if (mTitleRecording == null) {
            return;
        }
        mTitleRecording.setTextColor(AppContext.getColor(R.color.colorDisabledText));
        mTitleRecording.setText(getTitleRecording());
        animAlphaView(mTitleNormal, false);
        animAlphaView(mTitleRecording, true);
        animAlphaView(mTitleTime, true);
        animAlphaView(mRecordButtonOutsideCircle, true);
        animAlphaView(mUpdateProgress, true);
        animScaleView(mDelete, false);
        animScaleView(mCommit, false);
        animChangeCenterStatus();
        animCenterInsideCircleBreathing();
    }

    private void readyStatus() {
        if (mTitleRecording == null) {
            return;
        }
        mTitleRecording.setTextColor(AppContext.getColor(R.color.colorDisabledText));
        mTitleRecording.setText(R.string.audio_record_finished);
        animAlphaView(mTitleNormal, false);
        animAlphaView(mTitleRecording, true);
        animAlphaView(mTitleTime, true);
        animAlphaView(mRecordButtonOutsideCircle, true);
        animAlphaView(mUpdateProgress, true);
        animScaleView(mDelete, true);
        animScaleView(mCommit, true);
        animChangeCenterStatus();
        cancelAnimCenterInsideCircleBreathing();
    }

    private void playingStatus() {
        if (mTitleRecording == null) {
            return;
        }
        mTitleRecording.setTextColor(AppContext.getColor(R.color.cpColorTertiaryText));
        mTitleRecording.setText(R.string.audio_record_finished);
        animAlphaView(mTitleNormal, false);
        animAlphaView(mTitleRecording, true);
        animAlphaView(mTitleTime, true);
        animAlphaView(mRecordButtonOutsideCircle, true);
        animAlphaView(mUpdateProgress, true);
        animScaleView(mDelete, true);
        animScaleView(mCommit, true);
        animChangeCenterStatus();
        animCenterInsideCircleBreathing();
        resetProgressBar((int) mDurationInMS);
        onPlay();
    }

    private void animChangeCenterStatus() {
        switch (mStatus) {
            case NORMAL:
                mCenterStatus.setImageResource(R.drawable.icon_audio_record_view_normal);
                break;
            case RECORDING:
                mCenterStatus.setImageResource(R.drawable.icon_audio_record_view_recording);
                break;
            case READY:
                mCenterStatus.setImageResource(R.drawable.icon_audio_record_view_ready);
                break;
            case PLAYING:
                mCenterStatus.setImageResource(R.drawable.icon_audio_record_view_playing);
                break;
        }
    }

    private void animScaleView(View view, boolean isShow) {
        if (isShow) {
            if (view.getVisibility() == View.VISIBLE) {
                return;
            }
            ViewAnimator.animate(view)
                    .scale(0, 1f)
                    .onStart(() -> view.setVisibility(View.VISIBLE))
                    .interpolator(new OvershootInterpolator())
                    .duration(200)
                    .start();
        } else {
            if (view.getVisibility() != View.VISIBLE) {
                return;
            }
            ViewAnimator.animate(view)
                    .scale(1f, 0f)
                    .onStop(() -> view.setVisibility(View.GONE))
                    .interpolator(new AccelerateDecelerateInterpolator())
                    .duration(200)
                    .start();
        }
    }

    private void animAlphaView(View view, boolean isShow) {
        if (isShow) {
            if (view.getVisibility() == View.VISIBLE) {
                return;
            }
            ViewAnimator.animate(view)
                    .alpha(0, 1f)
                    .onStart(() -> view.setVisibility(View.VISIBLE))
                    .interpolator(new AccelerateDecelerateInterpolator())
                    .duration(100)
                    .start();
        } else {
            if (view.getVisibility() != View.VISIBLE) {
                return;
            }
            ViewAnimator.animate(view)
                    .alpha(1f, 0f)
                    .onStop(() -> view.setVisibility(View.GONE))
                    .interpolator(new AccelerateDecelerateInterpolator())
                    .duration(100)
                    .start();
        }
    }

    private void animCenterInsideCircle() {
        ViewAnimator.animate(mRecordButton)
                .scale(1f, 0.8f, 1f)
                .interpolator(new AccelerateDecelerateInterpolator())
                .duration(240)
                .start();
    }

    private void animCenterInsideCircleBreathing() {
        mInsideCircleBreathing = ViewAnimator.animate(mRecordButton)
                .scale(1f, 0.8f, 1f)
                .interpolator(new AccelerateDecelerateInterpolator())
                .duration(1500)
                .repeatMode(ViewAnimator.REVERSE)
                .repeatCount(ViewAnimator.INFINITE)
                .start();
    }

    private void vibrator() {
        if (mVibrator != null) {
            mVibrator.vibrate(50);
        }
    }

    private void cancelAnimCenterInsideCircleBreathing() {
        if (mInsideCircleBreathing != null) {
            mInsideCircleBreathing.cancel();
        }
    }

    private String getTitleRecording() {
        if (mRecordButton.hasPerformedLongClick()) {
            return getString(R.string.audio_record_long_press_tip);
        } else {
            return getString(R.string.audio_record_click_tip);
        }
    }

    @SuppressWarnings("all")
    private void tryStartRecord() {
        if (getActivity() == null) {
            return;
        }
        if (mRxPermissions == null) {
            mRxPermissions = new RxPermissions(getActivity());
        }
        boolean isGranted = ActivityCompat.checkSelfPermission(getActivity(), Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED;

        mRxPermissions.request(Manifest.permission.RECORD_AUDIO)
                .subscribe(granted -> {
                    if (granted) {
                        if (!isGranted) {
                            return;
                        }
                        startRecord();
                    } else {
                        GlobalUI.getInstance().showToast(getString(R.string.permission_audio_record));
                    }
                });
    }

    @SuppressWarnings("all")
    private void startRecord() {
        mAudioResultEvent = null;
        recordReset();

        mFile = FileCreateUtils.createAudioFile();
        mMP3Recorder = new MP3RecorderUtils(mFile);
        mMP3Recorder.setRecordStatusListener(this);
        try {
            mMP3Recorder.start();
            vibrator();
            recordTime();
            changeStatus(RECORDING);
        } catch (IllegalStateException | IOException e) {
            e.printStackTrace();
            changeStatus(NORMAL);
            GlobalUI.getInstance().showToast("error");
        }
    }

    @SuppressWarnings("all")
    private void stopRecord(boolean isSave, boolean isCommit) {
        disposable();

        if (mMP3Recorder == null) {
            changeStatus(NORMAL);
            return;
        }

        mMP3Recorder.setRecordStatusListener(null);
        if (mMP3Recorder.isRecording()) {
            mMP3Recorder.stop();
        }
        if (!isSave) {
            changeStatus(NORMAL);
        } else if (mDurationInMS < 1000) {
            GlobalUI.getInstance().showToast(getString(R.string.audio_record_top_short_tip));
            changeStatus(NORMAL);
        } else {
            Single.timer(128, TimeUnit.MILLISECONDS)
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(aLong -> {
                        mAudioResultEvent = new AudioResultEvent(requestTag, mFile == null ? "" : mFile.getPath(), mDurationInMS);
                        changeStatus(READY);
                        if (isCommit) {
                            commit();
                        }
                    }, Throwable::printStackTrace);
        }
    }

    private void recordTime() {
        mTimeDisposable = Flowable.interval(0, MAX_DURATION_IN_SECOND, TimeUnit.MILLISECONDS)
                .onBackpressureDrop()
                .compose(RxUtils.ioToMainFlowableScheduler())
                .subscribe(aLong -> {
                    mDurationInMS += MAX_DURATION_IN_SECOND;
                    mUpdateProgress.setProgress(aLong.intValue());

                    long durationInS = mDurationInMS / 1000;
                    String time = durationInS + " s";
                    mTitleTime.setText(time);

                    if (durationInS >= MAX_DURATION_IN_SECOND) {
                        stopRecord(true, false);
                    }
                });
    }

    private void onPlay() {
        final long totalDurationInS = (mDurationInMS / 1000);

        mTimeDisposable = Flowable.interval(0, totalDurationInS, TimeUnit.MILLISECONDS)
                .onBackpressureDrop()
                .compose(RxUtils.ioToMainFlowableScheduler())
                .subscribe(aLong -> {
                    mPlayDurationInMS += totalDurationInS;
                    mUpdateProgress.setProgress((int) mPlayDurationInMS);

                    String time = mPlayDurationInMS / 1000 + " s";
                    mTitleTime.setText(time);

                    if (mPlayDurationInMS >= mDurationInMS) {
                        stopPlay();
                    }
                });
    }

    private void stopPlay() {
        RxUtils.disposable(mTimeDisposable);
        if (mAudioResultEvent != null) {
            MP3PlayerUtils.getInstance().stop();
            changeStatus(READY);
        }
    }

    private void recordReset() {
        disposable();
        mTitleTime.setText("0 s");
        resetProgressBar(MAX_PROGRESS);
        mUpdateProgress.setProgress(0);
        mDurationInMS = 0;
        mFile = null;
    }

    private void resetProgressBar(int max) {
        mPlayDurationInMS = 0;
        mUpdateProgress.setMax(max);
        mUpdateProgress.setProgress(0);
    }

    private void disposable() {
        RxUtils.disposable(mTimeDisposable);
        cancelAnimCenterInsideCircleBreathing();
    }

    @Override
    public void onStartRecord() {
        tryStartRecord();
    }

    @Override
    public void onMoveToCancel() {
        mTitleRecording.setTextColor(AppContext.getColor(R.color.cpColorAccentRed));
        mTitleRecording.setText(R.string.audio_record_long_press_up_slide_tip);
    }

    @Override
    public void onMoveToContinue() {
        mTitleRecording.setTextColor(AppContext.getColor(R.color.cpColorTertiaryText));
        mTitleRecording.setText(R.string.audio_record_long_press_tip);
    }

    @Override
    public void onStopRecord(boolean isCommit) {
        if (isCommit) {
            stopRecord(true, true);
        } else {
            stopRecord(false, false);
            delete();
        }
    }

    @Override
    public void onLongClickChanged(boolean isLongClicked) {
        if (mAudioRecordLongClickChangedListener != null) {
            mAudioRecordLongClickChangedListener.onLongClickChanged(isLongClicked);
        }
    }

    public void setLongClickListener(AudioRecordLongClickChangedListener listener) {
        mAudioRecordLongClickChangedListener = listener;
    }

    @Override
    public void onDisable() {
        AppThreads.runOnMainThread(() -> {
            GlobalUI.getInstance().showToast(getString(R.string.audio_record_error));
            stopRecord(false, false);
        });
    }

    public void setRequestTag(@NotNull Object requestTag) {
        this.requestTag = requestTag;
    }

    public interface AudioRecordLongClickChangedListener {
        void onLongClickChanged(boolean isLongClicked);
    }
}
